package com.csj.mvvm.ui.base

import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentFactory
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.viewbinding.ViewBinding
import com.csj.mvvm.api.error.ErrorResult
import com.csj.mvvm.event.EventMessage
import com.csj.mvvm.utils.CustomDialog
import com.csj.mvvm.utils.LogUtil
import com.csj.mvvm.utils.ToastUtil
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import java.lang.reflect.ParameterizedType

/**
 * 包含ViewModel，View不使用反射
 */
abstract class BaseFragment2<VM : BaseViewModel, VB : ViewDataBinding> : Fragment() {

    lateinit var mContext: FragmentActivity
    var contentView: View? = null
    lateinit var vm: VM
    lateinit var v: VB

    private var loadingDialog: Dialog? = null
    private var builder: CustomDialog.Builder? = null
    var canCancel = true

    //Fragment的View加载完毕的标记
    private var isViewCreated = false

    //Fragment是否懒加载过
    private var isLazyLoaded = false

    companion object {

        /**
        val a = newFragment<TestF>(
        bundleOf(
        Pair("KEY_PRICE", 50.0),
        Pair("KEY_IS_FROZEN", false)
        )
        )
         */
        inline fun <reified F : Fragment> Context.newFragment(args: Bundle?): F {
//            return instantiate(this, F::class.java.name, args) as F
            val className = F::class.java.name
            val clazz = FragmentFactory.loadFragmentClass(
                classLoader, className
            )
            val f = clazz.getConstructor().newInstance()
            if (args != null) {
                args.classLoader = f.javaClass.classLoader
                f.arguments = args
            }
            return f as F
        }

        /**
        val v = newFragment<TestF>(
        Pair("KEY_PRICE", ""),
        Pair("KEY_PRICE", ""),
        Pair("KEY_PRICE", "")
        )
         */
        inline fun <reified F : Fragment> Context.newFragment(vararg args: Pair<String, String>): F {
            val bundle = Bundle()
            args.let {
                for (arg in args) {
                    bundle.putString(arg.first, arg.second)
                }
            }
            return instantiate(this, F::class.java.name, bundle) as F
        }
    }

    @Suppress("UNCHECKED_CAST")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //注意 type.actualTypeArguments[0]=BaseViewModel，type.actualTypeArguments[1]=ViewBinding
        val type = javaClass.genericSuperclass as ParameterizedType
        val clazz1 = type.actualTypeArguments[0] as Class<VM>
        vm = ViewModelProvider(this).get(clazz1)

        mContext = activity as FragmentActivity
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        if (null == contentView) {
            v = DataBindingUtil.inflate(inflater, getLayoutId(), container, false)
            v.lifecycleOwner = this
            contentView = v.root
            init()
            initData()
            initClick()
            initVM()
            LogUtil.e(getClassName())
        }

        return contentView
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        isViewCreated = true
        lazyLoad()
    }

    private fun init() {
        EventBus.getDefault().register(this)
        //loading
        vm.isShowLoading.observe(this, Observer {
            if (it) showLoading() else dismissLoading()
        })
        //错误信息
        vm.errorData.observe(this, Observer {
            if (it.show) ToastUtil.showToast(it.errMsg)
            errorResult(it)
        })
    }

    override fun onDestroy() {
        super.onDestroy()
        EventBus.getDefault().unregister(this)
    }

    //事件传递
    @Subscribe
    fun onEventMainThread(msg: EventMessage) {
        handleEvent(msg)
    }

    open fun getClassName(): String? {
        val className = "BaseFragment"
        try {
            return javaClass.name
        } catch (e: Exception) {
        }
        return className
    }

    abstract fun getLayoutId(): Int

    abstract fun initVM()

    abstract fun initClick()

    abstract fun initData()

    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        super.setUserVisibleHint(isVisibleToUser)
        //isVisibleToUser这个boolean值表示:该Fragment的UI 用户是否可见
        LogUtil.e("isVisibleToUser=" + isVisibleToUser)
        lazyLoad()
    }

    fun lazyLoad() {
        //这里进行双重标记判断,是因为setUserVisibleHint会多次回调,并且会在onCreateView执行前回调,必须确保onCreateView加载完毕且页面可见,才加载数据
        if (userVisibleHint && isViewCreated && !isLazyLoaded) {
            lazyLoadData()
            //数据加载完毕,恢复标记,防止重复加载
            isLazyLoaded = true
        }
    }

    //需要懒加载的数据，重写此方法
    abstract fun lazyLoadData()

    fun showLoading(msg: String) {
        if (loadingDialog == null) {
            builder = CustomDialog.Builder(mContext).setCancelable(canCancel)
            loadingDialog = builder?.createLoading(msg)
        }
        builder?.startAnimation()
        loadingDialog?.show()
    }

    fun showLoading() {
        if (loadingDialog == null) {
            builder = CustomDialog.Builder(mContext).setCancelable(canCancel)
            loadingDialog = builder?.createLoading("")
        }
        builder?.startAnimation()
        loadingDialog?.show()
    }

    fun dismissLoading() {
        if (loadingDialog != null) {
            loadingDialog?.dismiss()
        }
    }

    /**
     * 消息、事件接收回调
     */
    open fun handleEvent(msg: EventMessage) {}

    /**
     * 接口请求错误回调
     */
    open fun errorResult(errorResult: ErrorResult) {}

    override fun onDestroyView() {
        super.onDestroyView()
        contentView = null
    }

    private fun hideInputMethod() {
        val inputMethodManager =
            mContext.getSystemService(AppCompatActivity.INPUT_METHOD_SERVICE) as InputMethodManager
        val view = mContext.currentFocus ?: return
        inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
    }
}
